#include "camera_cv.h"
#include <QtSerialPort\QtSerialPort>
#include "CameraDS.h"

#define MIN(a,b) ((a)<(b))?(a):(b)
#define MAX(a,b) ((a)>(b))?(a):(b)

void PressLabel::mousePressEvent(QMouseEvent* ev)
{
	const QPoint p = ev->pos();
	emit mousePressed(p);
}

PressLabel::PressLabel(QWidget * parent, Qt::WindowFlags f)
	: QLabel(parent, f) {}

PressLabel::PressLabel(const QString& text, QWidget* parent, Qt::WindowFlags f)
	: QLabel(text, parent, f) {}

camera_cv::camera_cv(QWidget *parent)
	: QWidget(parent)
{
	ui.setupUi(this);
	setFixedSize(800, 600);
	QVBoxLayout *layout = new QVBoxLayout;
	QHBoxLayout *bottomLayout = new QHBoxLayout;	
	QHBoxLayout *bottomLayout2 = new QHBoxLayout;
	captureButton = new QPushButton("Capture");
	showGridButton = new QPushButton("Show Grid");
	startVideoButton = new QPushButton("Start Video");
	comButton = new QPushButton("Select COM");
	QObject::connect(captureButton, SIGNAL(clicked()), this, SLOT(capture()));
	QObject::connect(showGridButton, SIGNAL(clicked()), this, SLOT(showGridButtonClicked()));
	//QObject::connect(startVideoButton, SIGNAL(clicked()), this, SLOT(startVideo()));
	currentText = new QLabel("Select a com to start...");
	calibrateButton = new QPushButton("Calibration Phase 2");
	QObject::connect(calibrateButton, SIGNAL(clicked()), this, SLOT(toggleCalibrating()));
	bottomLayout2->addWidget(captureButton);
	calibrateButton->setEnabled(false);
	bottomLayout2->addWidget(currentText);
	bottomLayout->addWidget(startVideoButton);
	bottomLayout->addWidget(showGridButton);
	QHBoxLayout *bottomLayout3 = new QHBoxLayout;
	calibrateStep1Button = new QPushButton("Calibration Phase 1");
	phase1FromFileButton = new QPushButton("Phase 1 From File");
	phase2FromFileButton = new QPushButton("Phase 2 From File");
	calibrateStep1Button->setEnabled(false);
	phase1FromFileButton->setEnabled(false);
	phase2FromFileButton->setEnabled(false);
	QObject::connect(calibrateStep1Button, SIGNAL(clicked()), this, SLOT(thePhase1()));
	QObject::connect(phase1FromFileButton, SIGNAL(clicked()), this, SLOT(phase1FromFile()));
	QObject::connect(phase2FromFileButton, SIGNAL(clicked()), this, SLOT(phase2FromFile()));
	bottomLayout3->addWidget(calibrateStep1Button);
	bottomLayout3->addWidget(phase1FromFileButton);
	bottomLayout3->addWidget(calibrateButton);
	bottomLayout3->addWidget(phase2FromFileButton);
	cameraLabel = new PressLabel(this);
	//QObject::connect(cameraLabel, SIGNAL(mousePressed(const QPoint&)), this, SLOT(pressed(const QPoint&)));
	cameraLabel->setFixedSize(800, 500);
	infoLabel = new QLabel("Info");
	infoLabel->setFixedWidth(80);
	QHBoxLayout *hBox = new QHBoxLayout;
	hBox->addWidget(infoLabel);
	hBox->addWidget(cameraLabel);
	layout->addLayout(hBox);
	QMenu *comMenu = new QMenu;
	QMenu *cvMenu = new QMenu;
	QList<QSerialPortInfo> list;
	list = QSerialPortInfo::availablePorts();
	for (size_t i = 0; i < list.length(); i++)
	{
		QAction *click = new QAction(list[i].portName(), this);
		QObject::connect(click, SIGNAL(triggered()), this, SLOT(comActionTriggerred()));
		comMenu->addAction(click);
	}
	CCameraDS m_CamDS;
	for (size_t i = 0; i < CCameraDS::CameraCount(); i++)
	{
		char szCamName[1024];
		int retval = m_CamDS.CameraName(i, szCamName, sizeof(szCamName));
		QAction *click = new QAction(szCamName, this);
		click->setData(i);
		QObject::connect(click, SIGNAL(triggered()), this, SLOT(cvActionTriggerred()));
		cvMenu->addAction(click);
	}
	comButton->setMenu(comMenu);
	startVideoButton->setMenu(cvMenu);
	bottomLayout->addWidget(comButton);
	layout->addLayout(bottomLayout);
	layout->addLayout(bottomLayout2);
	layout->addLayout(bottomLayout3);
	setLayout(layout);
}

void camera_cv::phase1FromFile(){
	QString path = QFileDialog::getOpenFileName(this, tr("Open Phase 1 Data"), ".", tr("Plain Text Files(*.txt)"));
	if (path.length() == 0) {
		QMessageBox::information(NULL, tr("Path"), tr("You didn't select any files."));
	}
	else {
		QFile file(path);
		if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
			QMessageBox::information(NULL, tr("File"), tr("Cannot open file."));
		}
		else {
			QList<QPair<QPair<int, int>, QList<int>>> phase1NewData;
			while (!file.atEnd()) {
				QByteArray line = file.readLine();
				QString str(line);
				QList<QString> compo = str.split(" ");
				QPair<QPair<int, int>, QList<int>> newData;
				int j = 0;
				for (int i = 0; i < compo.length(); i++)
				{
					bool success;
					int k = compo[i].toInt(&success);
					if (success)
					{
						if (j == 0)
						{
							newData.first.first = k;
						}
						else if (j == 1) {
							newData.first.second = k;
						}
						else {
							newData.second.append(k);
						}
						j++;
						/*
						if (j == 7)
						{
							break;
						}*/
					}
				}
				if (newData.second.length() >= 5)
				{
					phase1NewData.append(newData);
				}
				qDebug() << str;
			}
			if (cvThread != NULL)
			{
				if (cvThread->setPhase1Data(std::move(phase1NewData))) {
					calibrateStep1Button->setText("Phase 1 Completed");
					phase1FromFileButton->setEnabled(false);
					calibrateStep1Button->setEnabled(false);
					calibrateButton->setEnabled(true);
					phase2FromFileButton->setEnabled(true);
					calibrateButton->setText("Calibration Phase 2");
				}
			}
		}
	}
}

void camera_cv::pressed(const QPoint& point) {
	if (cvThread != NULL)
	{
		cvThread->commitCalibration(point.x(), point.y());
	}
}

void camera_cv::toggleCalibrating() {
	if (cvThread != NULL)
	{
		bool result = cvThread->toggleCalibrating();
		calibrateButton->setText(result ? "Stop Calibrating" : "Phase 2 Completed");
		result ? QObject::connect(cameraLabel, SIGNAL(mousePressed(const QPoint&)), this, SLOT(pressed(const QPoint&))) : QObject::disconnect(cameraLabel, SIGNAL(mousePressed(const QPoint&)), this, SLOT(pressed(const QPoint&)));
		if (!result)
		{
			calibrateButton->setEnabled(false);
			phase2FromFileButton->setEnabled(false);
		}
	}
	else {
		QObject::disconnect(cameraLabel, SIGNAL(mousePressed(const QPoint&)), this, SLOT(pressed(const QPoint&)));
		calibrateButton->setEnabled(false);
		phase2FromFileButton->setEnabled(false);
	}
}

void camera_cv::cvActionTriggerred() {
	QAction *action = qobject_cast<QAction *>(sender());
	qDebug() << action->data().value<int>();
	if (cvThread != NULL)
	{
		startVideoButton->setText("Start Video");
		//calibrateButton->setText("Calibrate");
		//calibrateButton->setEnabled(false);
		cvThread->stop();
		cvThread = NULL;
	}
	else {
		startVideoButton->setText("Stop Video");
		//calibrateButton->setText("Calibrate");
		//calibrateButton->setEnabled(true);
		cvThread = new CvThread;
		//QObject::disconnect(cvThread, SIGNAL(gotNewData(QImage)), this, SLOT(gotNewImage(QImage)));
		QObject::connect(cvThread, SIGNAL(gotNewData(QImage)), this, SLOT(gotNewImage(QImage)));
		QObject::connect(cvThread, SIGNAL(phase1(int,int)), this, SLOT(phase1(int, int)));
		QObject::connect(cvThread, SIGNAL(sendInfo(QString)), this, SLOT(gotInfo(QString)));
		cvThread->setId(action->data().value<int>());
		cvThread->start();
	}
	if (cvThread != NULL && comThread != NULL) {
		calibrateButton->setText("Calibration Phase 2");
		calibrateStep1Button->setText("Calibration Phase 1");
		calibrateStep1Button->setEnabled(true);
		phase1FromFileButton->setEnabled(true);
	}
	else {
		calibrateStep1Button->setEnabled(false);
		phase1FromFileButton->setEnabled(false);
	}
}

void camera_cv::capture() {
	if (cameraLabel->pixmap())
	{
		RecordWidget *record = new RecordWidget;
		record->setup(cameraLabel->pixmap()->toImage(), currentText->text());
		record->show();
	}
}

camera_cv::~camera_cv()
{
	
}

void camera_cv::showGridButtonClicked() 
{
	if (cvThread != NULL)
	{
		bool result = cvThread->toggleGrid();
		QString title = showGridButton->text();
		showGridButton->setText(result ? "Hide Grid":"Show Grid");
	}
	else 
	{
		showGridButton->setText("Show Grid");
	}
}

void camera_cv::gotCom(int x, int y) {
	if (cvThread != NULL)
	{
		//cvThread->setXY(x, y);
	}
	QString text = QString::number(x) + " " + QString::number(y);
	currentText->setText(text);
}


void camera_cv::phase1(int x, int y) {
	calibrateStep1Button->setText(QString("%1 %2").arg(x).arg(y));
}

void camera_cv::comActionTriggerred() 
{
	QAction *action = qobject_cast<QAction *>(sender());
	qDebug() << action->text();
	if (comThread != NULL)
	{
		comThread->stop();
		comThread = NULL;
	}
	else 
	{
		comThread = new ComThread;
		//QObject::disconnect(comThread, SIGNAL(getComData(int, int)), this, SLOT(void gotCom(int, int)));
		QObject::connect(comThread, SIGNAL(getComData(int, int)), this, SLOT(gotCom(int, int)));
		QObject::connect(comThread, SIGNAL(getComData(QList<int>)), this, SLOT(gotComData(QList<int>)));
		QObject::connect(comThread, SIGNAL(getComInfo(QList<int>)), this, SLOT(gotComInfo(QList<int>)));
		QObject::connect(comThread, SIGNAL(sendInfo(QString)), this, SLOT(gotInfo(QString)));
		comThread->portName = action->text();
		comThread->start();
	}
	if (cvThread != NULL && comThread != NULL) {
		calibrateButton->setText("Calibration Phase 2");
		calibrateStep1Button->setText("Calibration Phase 1");
		calibrateStep1Button->setEnabled(true);
		phase1FromFileButton->setEnabled(true);
	}
	else {
		calibrateStep1Button->setEnabled(false);
		phase1FromFileButton->setEnabled(false);
	}
}

void camera_cv::gotNewImage(QImage image) {
	/*
	int width = QApplication::desktop()->width();
	int height = QApplication::desktop()->height();
	if ((image.width()+20) > width*4/5 || (image.height() + 80) > height*4/5)
	{
		//image = image.scaled(width * 4 / 5 - 20, height * 4 / 5 - 80);
	}*/
	setFixedSize(image.width() + 100, image.height() + 120);
	cameraLabel->setAlignment(Qt::AlignCenter);
	cameraLabel->setFixedSize(image.width(), image.height());
	cameraLabel->setPixmap(QPixmap::fromImage(image));
}

void camera_cv::startVideo() {
	if (cvThread != NULL)
	{
		startVideoButton->setText("Start Video");
		cvThread->stop();
		cvThread = NULL;
	}
	else {
		startVideoButton->setText("Stop Video");
		cvThread = new CvThread;
		//QObject::disconnect(cvThread, SIGNAL(gotNewData(QImage)), this, SLOT(gotNewImage(QImage)));
		QObject::connect(cvThread, SIGNAL(gotNewData(QImage)), this, SLOT(gotNewImage(QImage)));
		QObject::connect(cvThread, SIGNAL(sendInfo(QString)), this, SLOT(gotInfo(QString)));
		cvThread->start();
	}
}

void camera_cv::gotInfo(QString text) {
	currentText->setText(text);
}

void camera_cv::thePhase1() {
	if (cvThread != NULL)
	{
		if (cvThread->calibratePhase1()) {
			calibrateStep1Button->setText("Phase 1 Completed");
			phase1FromFileButton->setEnabled(false);
			calibrateStep1Button->setEnabled(false);
			calibrateButton->setEnabled(true);
			phase2FromFileButton->setEnabled(true);
			calibrateButton->setText("Calibration Phase 2");
		}
	}
}

bool camera_cv::close() {
	if (cvThread != NULL) {
		cvThread->stop();
	}
	if (comThread != NULL)
	{
		comThread->stop();
	}
	delete comThread;
	comThread = NULL;
	delete cvThread;
	cvThread = NULL;
	return QWidget::close();
}


void camera_cv::gotComData(QList<int> list) {
	if (cvThread != NULL)
	{
		if (list.length() >= 2)
		{
			cvThread->enqueueRawData(list);
			//cvThread->setXY(list[0], list[1]);
			//QString text = QString::number(list[0]) + " " + QString::number(list[1]);
			//currentText->setText(text);
		}
	}
}

void camera_cv::gotComInfo(QList<int> list) {
	if (list.length() >= 2)
	{
		infoLabel->setText(QString("Battery : %1\r\nRSSI : %2").arg(list[0]).arg(list[1]));
	}
}


void camera_cv::phase2FromFile() {
	QString path = QFileDialog::getOpenFileName(this, tr("Open Phase 2 Data"), ".", tr("Plain Text Files(*.txt)"));
	if (path.length() == 0) {
		QMessageBox::information(NULL, tr("Path"), tr("You didn't select any files."));
	}
	else {
		QFile file(path);
		if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
			QMessageBox::information(NULL, tr("File"), tr("Cannot open file."));
		}
		else {
			double offsetX = 0;
			double offsetY = 0;
			int count = 0;
			while (!file.atEnd()) {
				QByteArray line = file.readLine();
				QString str(line);
				QList<QString> compo = str.split(" ");
				QList<double> compoDouble;
				for (QString singleStr : compo)
				{
					bool success;
					double res = singleStr.toDouble(&success);
					if (success)
					{
						compoDouble.append(res);
					}
				}
				if (compoDouble.length() >= 4)
				{
					offsetX += (compoDouble[2] - compoDouble[0]);
					offsetY += (compoDouble[3] - compoDouble[1]);
					count ++;
				}
			}
			if (count > 0)
			{
				offsetX /= (double)count;
				offsetY /= (double)count;
				if (cvThread != NULL)
				{
					cvThread->setPhase2Data(offsetX, offsetY);
					calibrateButton->setText("Phase 2 Completed");
					phase2FromFileButton->setEnabled(false);
					calibrateButton->setEnabled(false);
				}
			}
		}
	}
}
